/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
package com.aljfce.business.event.control;

import com.aljfce.business.calendar.control.CalendarManager;
import com.aljfce.business.entity.Calendar;
import javax.ejb.Stateful;
import com.aljfce.business.entity.Event;
import com.aljfce.business.entity.Notification;
import com.aljfce.business.entity.User;
import com.aljfce.business.entity.WeatherInformation;
import java.util.Date;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.annotation.Resource;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import com.aljfce.business.forecast.control.ForecastManager;
import com.aljfce.business.forecast.exception.DayOutOfRangeException;
import com.aljfce.business.forecast.exception.ForecastNotFoundException;
import com.aljfce.business.mail.control.MailManager;
import com.aljfce.business.notification.control.NotificationManager;
import com.aljfce.business.profile.boundary.GuestHome;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Objects;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import javax.ejb.EJB;
import javax.ejb.Stateless;
import javax.inject.Inject;
import javax.inject.Named;

/**
 *
 * @author EOroan
 */
@Named
@Stateless
public class EventManager {

    @Inject
    private NotificationManager nm;

    @Inject
    private ForecastManager fm;
    @Inject
    private CalendarManager cm;

    @Inject
    private MailManager mm;

    private Event event;

    private Calendar calendar;

    @PersistenceContext(unitName = "meteocalPU")
    private EntityManager em;

    private User user;

    /**
     * Get the value of user
     *
     * @return the value of user
     */
    public User getUser() {
        return user;
    }

    /**
     * Set the value of user
     *
     * @param user new value of user
     */
    public void setUser(User user) {
        this.user = user;
    }

    /**
     * Get the value of calendarL
     *
     * @return the value of calendarL
     */
    public Calendar getCalendar() {
        return calendar;
    }

    private Notification.TYPE invitation;

    /**
     * Get the value of invitations
     *
     * @return the value of invitations
     */
//    public Notification.TYPE getInvitation() {
//        return invitation;
//    }
    /**
     * Set the value of invitations
     *
     * @param invitation new value of invitations
     */
    public void setInvitation(Notification.TYPE invitation) {
        this.invitation = invitation;
    }

    @PostConstruct
    public void init() {

    }

    /**
     * <b>controlEvent</b> controls whether or not there are events overlapping
     * with <b>event</b>
     *
     * @param user Is the creator of the event to control. This is always the
     * user of associated to the the class {@link Principal}.
     * @param event To control.
     * @param users It is a set of users to invite at the event
     * @return The set of events presents in creator's calendar (including the
     * ones created by him and those he accepted to participate) overlapping
     * with the event controlled, if the event start date is before its end date
     * @throws DayOutOfRangeException When the start date of the event is far in
     * the future
     * @throws IllegalArgumentException When there is a parameter null
     * @throws ForecastNotFoundException When the Weather forecast is not
     * available for the start time and the location of the event
     */
    public ArrayList<Event> controlEvent(User user, Event event, ArrayList<User> users)
            throws DayOutOfRangeException, IllegalArgumentException, ForecastNotFoundException {
        if (event.getStartingDate().before(event.getEndingTime())) {

            ArrayList<Event> coincidentEvents = controlOverlapping(user, event); //TO REPLACE THE FOLLOWING AFTER THE OVERLA CONTROL

            if (!coincidentEvents.isEmpty()) {
                //showOverlap
                return coincidentEvents;
            } else {
                //createEvent or updateEvent
                if (event.getId() != null) {
                    updateEvent(user, event, users);
                    return null;
                } else {
                    createEvent(user, event, users);
                    return null;
                }
            }
        }
        return null;
    }

    /**
     * Set the value of calendarL
     *
     * @param calendar new value of calendarL
     */
    public void setCalendar(Calendar calendar) {
        this.calendar = calendar;
    }

    /**
     * Get the value of event
     *
     * @return the value of event
     */
    public Event getEvent() {
        return event;
    }

    /**
     * Set the value of event
     *
     * @param evnt new value of event
     */
    public void setEvent(Event evnt) {
        this.event = evnt;
    }

    /**
     *
     * @param user Creator of the event
     * @param event Determine whether or not there are events overlapping with
     * the event in parameter
     * @return Set of event overlapping
     */
    public ArrayList<Event> controlOverlapping(User user, Event event) {

        Calendar calendarL = cm.getCalendar(user.getEmail());

        List<Event> events = (List<Event>) calendarL.getEventCollection();
        ArrayList<Event> coincidentEvents = new ArrayList<>();

        for (Event event1 : events) {
            if ((event.getEndingTime().after(event1.getStartingDate()) && event.getEndingTime().before(event1.getEndingTime()))
                    || (event.getStartingDate().after(event1.getStartingDate()) && event.getStartingDate().before(event1.getEndingTime()))
                    || (event.getStartingDate().before(event1.getStartingDate()) && event.getEndingTime().after(event1.getEndingTime()) /*control if it's necessery*/)
                    || (event.getStartingDate().after(event1.getStartingDate()) && event.getEndingTime().before(event1.getEndingTime()) /*control if it's necessery*/)
                    || (event.getStartingDate().equals(event1.getStartingDate()))
                    || (event.getStartingDate().equals(event1.getEndingTime()))
                    || (event.getEndingTime().equals(event1.getStartingDate()))
                    || (event.getEndingTime().equals(event1.getEndingTime()))) {

                if (!Objects.equals(event.getId(), event1.getId())) {
                    coincidentEvents.add(event1);
                }
            }
        }
        return coincidentEvents;
    }

    /**
     *
     * @param user Creator of the event in parameter
     * @param event To be create. A new event is created and made persistent to
     * the database. It is added in the {@link Calendar} associated the user
     * creator
     * @param users That are invited to event in parameter
     */
    public void createEvent(User user, Event event, ArrayList<User> users) {
        Notification inviTation = new Notification();
        Collection<Notification> invitations = new ArrayList<>();

        event.setNotificationCollection(invitations);
        Collection<WeatherInformation> CollwInf = new ArrayList<>();
        if (event.getOutdoor()) {
            WeatherInformation wInf = null;
            try {
                wInf = fm.getForecast(event.getLocation(), event.getStartingDate());
            } catch (DayOutOfRangeException | ForecastNotFoundException f) {
                Logger.getLogger(EventManager.class.getName()).log(Level.SEVERE, null, f);
            } catch (Exception e) {
                Logger.getLogger(EventManager.class.getName()).log(Level.SEVERE, "Illegal argument to forecast Manager");
            }
            if (wInf != null) {
                WeatherInformation controlWI = em.find(WeatherInformation.class, wInf.getWeatherInformationPK());
                if (controlWI == null) {
                    CollwInf.add(wInf);
                    em.persist(wInf);
                } else {
                    em.merge(wInf);
                }
            }
        }
        event.setWeatherInformationCollection(CollwInf);
        event.setCreator(user);
        //user.getCalendarCollection().g ADD THE EVENT TO CALENDAR OF THE USER!!!!!!!!!!!!!!
        //FIXME problem with cm.getCalendar not working in this context and when the event is in the past
        Calendar calendarCurrent = cm.getCalendar(user.getEmail());
        calendarCurrent.addEvent(event);

        for (User user1 : users) {
            inviTation = nm.newNotification(Notification.TYPE.INVITATION_NOTIFICATION, user1, event, null, Boolean.FALSE);
            if (inviTation != null) {
                em.persist(inviTation);
                invitations.add(inviTation);
                mm.sendInvitationNotificationMsg(user1, event);
            }
        }

        em.persist(event);
        em.merge(calendarCurrent);
        //TODO - call boundary to show userhome
    }

    /**
     * <b>updateEvent</b> modifies an <b>event</b> already existing
     *
     * @param user That updates the event in parameter. That event has been
     * created using the {@link createEvent} method
     * @param event That is actually existing, and that is to be updated
     * @param users To invite to the event to update
     */
    public void updateEvent(User user, Event event, ArrayList<User> users) {
        Notification inviTation = new Notification();
        Collection<Notification> invitations = event.getNotificationCollection();

        ArrayList<WeatherInformation> newWeatherInformations = new ArrayList<>();
        if (event.getOutdoor()) {
            //collection of event's weather informations
            ArrayList<WeatherInformation> weatherInformations = new ArrayList<>(event.getWeatherInformationCollection());
            //new weather information
            WeatherInformation wInf = null;

            try {
                //this is the new weather information
                wInf = fm.getForecast(event.getLocation(), event.getStartingDate());
            } catch (DayOutOfRangeException | IllegalArgumentException | ForecastNotFoundException ex) {
                Logger.getLogger(EventManager.class.getName()).log(Level.SEVERE, null, ex);
            }

            //if the weather information exists 
            if (wInf != null) {
                //if not exists in database is null
                WeatherInformation controlWI = em.find(WeatherInformation.class, wInf.getWeatherInformationPK());

                if (controlWI == null) {

                    newWeatherInformations.add(wInf);
                    em.merge(wInf);
                } else {
                    newWeatherInformations.add(wInf);
                }
            }
        }
        event.setWeatherInformationCollection(newWeatherInformations);
        Calendar calendarCurrent = cm.getCalendar(user.getEmail());

        for (User user1 : users) {
            inviTation = nm.newNotification(Notification.TYPE.INVITATION_NOTIFICATION, user1, event, null, Boolean.FALSE);
            if (inviTation != null) {
                em.persist(inviTation);
                invitations.add(inviTation);
                mm.sendInvitationNotificationMsg(user1, event);
            }
        }

        em.merge(event);
        em.merge(calendarCurrent);
        //TODO - call boundary to show userhome

    }

//    public void loadMyEvent(int eventId/*, User user*/) {
//        List<Event> events = em.createNamedQuery("Event.findById", Event.class)
//                .setParameter("id", eventId).getResultList();
//        User eventCreator = events.get(0).getCreator();//this one should be the user-property of this EventManager = user logged in
//        event = events.get(0);
//        if (eventCreator.equals(user)) {
//            //call boundary to show OwnEvent
//        } else {
//            //call boundary to show AcceptedEvent
//        }
//    }
//    public Event loadOtherEvent(int eventId/*, User user*/) {
//        List<Event> events;
//        events = (List<Event>) calendar.getEventCollection();
//        for (int i = 0; i < events.size(); i++) {
//            if (events.get(i).getId() == eventId) {
//                return events.get(i);
//            }
//        }
//        return null;
//    }
    /**
     * Delete an event created
     *
     * @param event An existing event is deleted from the database and is no
     * more usable
     * @param user That created the event.
     */
    public void deleteEvent(Event event, User user) {
        Calendar calendarCurrent = cm.getCalendar(user.getEmail());
        calendarCurrent.removeEvent(event.getId());
        event = em.find(Event.class, event.getId());
        WeatherInformation Winf = event.getWeatherInformation();
        if (Winf != null && (Winf.getEventCollection().size() == 1)) {
            em.remove(Winf);
        }
        em.remove(event);
        em.merge(calendarCurrent);
        //call boundary to show userHome
    }

    /**
     * used to remove an event accepted
     *
     * @param eventId
     */
    public void removeEvent(int eventId) {
    }

}
